import java.util.*;

class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
class Message<T> {
    private T message ;
    public T getMessage() {
        return message;
    }
    public void setMessage(T message) {
        this.message = message;
    }
}
class Food {
}
class Fruit extends Food {
}
class Apple extends Fruit {
}
class Banana extends Fruit {
}
class Plate<T> {
    private T plate ;
    public T getPlate() {
        return plate;
    }
    public void setPlate(T plate) {
        this.plate = plate;
    }
}
public class Test {
    /**
     * 泛型的进阶
     * @param args
     *      通配符
     */
    /**
     * 通配符
     *      1.什么情况下使用通配符->当不确定泛型参数是什么引用类型时,使用通配符 ?
     *      2.泛型上界
     *      3.泛型下界
     */

    public static void fun(Plate<? super Fruit> temp){
// 此时可以修改！！添加的是Fruit 或者Fruit的子类
        temp.setPlate(new Apple());//这个是Fruit的子类
        temp.setPlate(new Fruit());//这个是Fruit的本身
//Fruit fruit = temp.getPlate(); 不能接收，这里无法确定是哪个父类
        System.out.println(temp.getPlate());//只能直接输出
    }
    public static void main(String[] args) {
        /**
         * 泛型下界
         *      语法:<? super 下界>->该泛型参数只能是下界/下界的父类
         *      只能进行改变值,但是无法获取到值
         */
        Plate<Fruit> plate1 = new Plate<>();
        plate1.setPlate(new Fruit());
        fun(plate1);
        Plate<Food> plate2 = new Plate<>();
        plate2.setPlate(new Food());
        fun(plate2);
    }
    public static void fun2(Message<? extends Fruit> temp){
        //temp.setMessage(new Banana()); //仍然无法修改！
        //temp.setMessage(new Apple()); //仍然无法修改！
        System.out.println(temp.getMessage());
    }
    public static void main5(String[] args) {
        /**
         * 泛型上界
         *      语法格式:<? extend 上界>->则泛型参数只能是上界/上界的子类
         *      可以进行接收类型,但是无法改变值,因为,不确定该泛型是什么引用类型
         */
        Message<Apple> message = new Message<>() ;
        message.setMessage(new Apple());
        fun2(message);
        Message<Banana> message2 = new Message<>() ;
        message2.setMessage(new Banana());
        fun2(message2);
    }
    public static void main4(String[] args) {
        Message<String> message1 = new Message<>();
        Message<Integer> message2 = new Message<>();
        message1.setMessage("世界");
        message2.setMessage(999);
        fun1(message1);
        fun1(message2);
    }
    public static void fun1(Message<?> temp) {
        System.out.println(temp.getMessage());
    }
    /**
     * 再谈String
     * @param args
     *      字符串常量池
     *      String对象的创建
     */
    public static void main3(String[] args) {
        /**
         * 再谈String对象的创建
         *      1.直接进行赋值->首先在常量池中寻找是否存在,存在,直接引用常量池已经存在的对象,不存在,则创建
         *      2.newString对象->创建一个新的对象(开辟空间)
         *      3.intern方法:手动将String对象放入常量池中
         */
    }

    public static void main2(String[] args) {
        /**
         * 字符串常量池:JVM中的StringTable类,实际上是一个固定大小的HashTable(一种数据结构)
         */
    }
    /**
     * 前K个高频单词
     * @param words
     * @param k
     * @return
     */
    public static List<String> topKFrequent(String[] words, int k) {
        //1.求单词出现次数
        Map<String,Integer> map = new HashMap<>();
        for(String str : words) {
            if(map.get(str) == null) {//第一次插入
                map.put(str,1);
            }else {
                int val = map.get(str);
                map.put(str,val + 1);
            }
        }
        //2.遍历 放入小根堆
        PriorityQueue<Map.Entry<String,Integer>> minHeap =
                new PriorityQueue<>(new Comparator<Map.Entry<String, Integer>>() {
                @Override
                public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                    if(o1.getValue().compareTo(o2.getValue()) == 0) {//次数相同
                        return o2.getKey().compareTo(o1.getKey());
                    }
                    return o1.getValue().compareTo(o2.getValue());
        }});
        //遍历map
        for(Map.Entry<String,Integer> entry : map.entrySet()) {
            if(minHeap.size() < k) {//堆没满
                minHeap.offer(entry);
            }else {//已满,进行比较
                Map.Entry<String,Integer> top = minHeap.peek();
                if(top.getValue().compareTo(entry.getValue()) < 0) {//top小
                    minHeap.poll();
                    minHeap.offer(entry);
                }else {//次数相同
                    if(top.getValue().compareTo(entry.getValue()) == 0) {//判断次序
                        if(top.getKey().compareTo(entry.getKey()) > 0) {//前者小
                            minHeap.poll();
                            minHeap.offer(entry);
                        }
                    }
                }
                //top大,不交换
            }
        }
        List<String> ret = new ArrayList<>();
        //遍历小根堆
        for(int i = 0;i < k;i++) {
            Map.Entry<String,Integer> top = minHeap.poll();
            ret.add(top.getKey());
        }

        Collections.reverse(ret);
        return ret;
    }
    /**
     * 旧键盘
     * @param args
     */
    public static void main1(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextLine()) { // 注意 while 处理多个 case
            String str1 = in.nextLine();
            String str2 = in.nextLine();
            func(str1,str2);
        }
    }
    /**
     */
    private static void func(String str1,String str2) {
        Set<Character> set = new HashSet<>();
        for(char s : str2.toUpperCase().toCharArray()) {
            set.add(s);
        }

        Set<Character> setBroken = new HashSet<>();
        for(char s : str1.toUpperCase().toCharArray()) {
            if(!set.contains(s) && ! setBroken.contains(s)) {
                System.out.print(s);
                setBroken.add(s);
            }
        }
    }

    /**
     * 随机链表的复制
     * @param head
     * @return
     */
    public Node copyRandomList(Node head) {
        Map<Node,Node> map = new HashMap<>();
        Node cur = head;
        while(cur != null) {
            Node node = new Node(cur.val);
            map.put(cur,node);
            cur = cur.next;
        }

        cur = head;
        while(cur != null) {
            map.get(cur).next = map.get(cur.next);
            map.get(cur).random = map.get(cur.random);
            cur = cur.next;
        }

        return map.get(head);
    }
}
